<template>
  <div class="footer">
    <div class="play-bar">
      <div>
        <el-button type="success" @click="openFileAd" :loading="isLoading" title="打开文件夹" :icon="House">打开文件夹</el-button>
        <el-button type="success" @click="startShot" :loading="isLoading" title="快捷键 ctrl + alt + p">截图</el-button>
        <el-button type="success" @click="startShotChromeImg" :loading="isLoading" title="浏览器截图"
          :class="[isShot ? 'arrow_box' : '']">网页存为图片</el-button>
        <el-button v-if="!isShowRecord" type="success" @click="startRecord" title="开始录屏">开始录屏</el-button>
        <el-button v-if="isShowRecord" type="warning" @click="endHandler(recorder)" title="停止录屏">停止录屏</el-button>
        <span v-if="isShowRecord" class="time">{{ callinTime }}</span>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { useShotStore } from "@/store/store";
import { storeToRefs } from "pinia";
import { House } from '@element-plus/icons-vue';
import { ref } from 'vue';
import { ipcRenderer } from "electron";
import { formatDate } from "@/util/util";
import { Buffer } from 'buffer';
const exec = require("child_process").exec;
const path = require("path");

const remote = require("@electron/remote")
const shotStore = useShotStore();
const { config, isLoading, fileList } = storeToRefs(shotStore);
const fs = require("fs");
const isShot = ref(false)
let timer: any = null
const callinTime = ref('')
const isShowRecord = ref(false)
let recorder: MediaRecorder | null = null
let chunks: any = []
const startRecord = () => {
  startHandler()
  getScreenId()
}

/* 计时 */
const startHandler = () => {
  isShowRecord.value = true
  start(true)
}
const start = (bool: any) => {
  let hour: any = 0,
    minute: any = 0,
    second: any = 0
  if (bool === true) {
    timer = setInterval(function () {
      if (second >= 0) {
        second = second + 1
      }
      if (second >= 60) {
        second = 0
        minute = minute + 1
      }
      if (minute >= 60) {
        minute = 0
        hour = hour + 1
      }
      callinTime.value = addZero(hour) + ':' + addZero(minute) + ':' + addZero(second)
    }, 1000)
  } else {
    window.clearInterval(timer)
    timer = null
  }
}
const addZero = (num: any) => {
  return num < 10 ? '0' + num.toString() : num.toString()
}
// 暂停计时
const endHandler = (rec: any) => {
  start(false)
  callinTime.value = '00:00:00'
  isShowRecord.value = false
  rec.stop()
}
/* 计时 */
/* 屏幕录制 */
const getScreenId = () => {
  ipcRenderer.send("getScreenId")
}
const getScreenStream = async (sourceId: string) => {
  /* 通过remote获取屏幕信息，宽高 */
  let screeninfo = remote.screen
  const allscreenInfo = screeninfo.getPrimaryDisplay().workAreaSize
  let option: any = {
    audio: false,
    video: {
      mandatory: {
        chromeMediaSource: "desktop",
        chromeMediaSourceId: sourceId,
        minWidth: allscreenInfo.width | 1920, // 使用主屏幕的大小作为默认大小，也可以改为用户自己设置大小
        maxWidth: allscreenInfo.width | 1920,
        minHeight: allscreenInfo.height | 1080,
        maxHeight: allscreenInfo.height | 1080,
        minFrameRate: 30, // 控制帧率
      }
    }
  }
  /* 获取屏幕的流 */
  const stream = await navigator.mediaDevices.getUserMedia(option);
  /* 音轨根据配置文件增加 */
  if (config.value.sysSound) {
    let audio: any = await getDisplayMediaSource('system')
    if (audio) {
      (audio.getVideoTracks() || []).forEach((track: any) => audio.removeTrack(track));
      // 将获取的音频流数据添加到视频流
      let allAudioTracks = audio.getAudioTracks();
      (allAudioTracks || []).forEach((track: any) => stream.addTrack(track));
    }
  }
  if (config.value.micSound) {
    let audio: any = await getDisplayMediaSource('microphone')
    if (audio) {
      (audio.getVideoTracks() || []).forEach((track: any) => audio.removeTrack(track));
      // 将获取的音频流数据添加到视频流
      let allAudioTracks = audio.getAudioTracks();
      (allAudioTracks || []).forEach((track: any) => stream.addTrack(track));
    }
  }
  createRecorder(stream)
}
// 获取麦克风音频或者系统音频方法。Win10上需要开启麦克风访问权限。否则无法访问
const getDisplayMediaSource = async (configVoiceType: string) => {
  let audioStream = null;
  // 选择录音条件，分为系统和麦克风
  let constraints: any = null;
  if (configVoiceType === "microphone") {
    constraints = {
      audio: true,
      video: false,
    };
  }
  if (configVoiceType === "system") {
    constraints = {
      audio: {
        mandatory: {
          chromeMediaSource: "desktop",
        },
      },
      video: {
        mandatory: {
          chromeMediaSource: "desktop",
        },
      },
    };
  }
  await navigator.mediaDevices.getUserMedia(constraints).then(function (audio) {
    /* 使用这个stream */
    audioStream = audio;
  })
    .catch(function (err) {
      /* 处理error，很可能是没有开启权限。 */
      console.log("获取麦克风失败：" + err);
      audioStream = null;
    });
  return audioStream;
}
/**
 * 开始录制视频   为了支持大于2G视频录制，看以下连接：
 * https://www.bilibili.com/read/cv14825350
 * */
const createRecorder = (stream: any) => {
  const mimeType = "video/webm;codecs=vp9";
  recorder = new MediaRecorder(stream, {
    mimeType,
    videoBitsPerSecond: 1e9, // 大约2.5Mbps的码率    // 属于标清范畴。
  }); // 参数列表： https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder

  // 缓存视频数据。
  recorder.ondataavailable = (e) => {
    if (!chunks) chunks = [];
    if (e.data.size > 0) {
      chunks.push(e.data);
    }
  };
  recorder.onstop = async (e) => {
    saveMedia(new Blob([...chunks]));
  };
  recorder.start(5000); // 每5秒触发一次ondataavailable事件。输出并清空缓冲区。（很重要，否则很容易内存溢出）
  recorder.onerror = (err) => {
    console.error(`录制视频失败${err}`);
  }
  return recorder;
}
const saveMedia = (blob: any) => {
  let reader: any = new FileReader();
  reader.readAsArrayBuffer(blob);

  reader.onload = () => {
    let buffer = Buffer.from(reader.result);
    fs.writeFile(`${config.value.savePath}/test.mp4`, buffer, {}, (err: any) => {
      if (err) {
        return console.error(err);
      } else {
        let timeNow = formatDate(Date.now(), true, ' ');
        if (fs.existsSync(`${config.value.savePath}/test.mp4`)) {
          // 使用ffmpeg修复视频的时间轴 ffmpeg -i <input-file> -codec copy <output-file> 使用此命令修复视频时间轴。
          new Promise((resolve, reject) => {
            exec(
              `${path.join(process.cwd(), "/public/ffmpeg.exe")} -i ${path.join(config.value.savePath, "/test.mp4")} -codec copy ${path.join(config.value.savePath, "/" + timeNow + ".mp4")}`.replace('m Files', '~1'),
              {
                encoding: "utf8",
                cwd: process.cwd(),
              },
              async (err: any, stdout: any, stderr: any) => {
                if (err) {
                  reject(err)
                } else {
                  chunks = [];
                  fs.unlinkSync(`${config.value.savePath}/test.mp4`);
                  console.log(`视频转换成功 : ${config.value.savePath}\\${timeNow}`);
                  resolve(true)
                }
              }
            ).on('exit', function (code: any) {
              console.log('子进程已退出1, 退出码 ' + code);
            });
          })
        }
      }
    });
  };
  reader.onerror = (err: any) => console.error(err);
}
/* 屏幕录制 */
const openFileAd = () => {
  ipcRenderer.send("openFileFolder", config.value.savePath)
};
const startShot = () => {
  ipcRenderer.send('startCapture')
}
const startShotChromeImg = () => {
  isShot.value = true
  ipcRenderer.send('startCaptureChromeImg', config.value.savePath)
}
ipcRenderer.on("startCapture-reply", (event: any, msg: any) => {
  let { buff, fileName } = msg
  let filePath = config.value.savePath + '\\' + fileName
  fs.writeFile(filePath, buff, (err: any) => {
    if (err) { console.log(err); return }
    fileList.value.push({ name: fileName, url: filePath })
  })
});
ipcRenderer.on("largeShotsRes", (event: any, msg: string) => {
  let filePath = msg
  let fileName = msg.split('\\')[msg.split('\\').length - 1]
  fileList.value.push({ name: fileName, url: filePath })
  isShot.value = false
});
ipcRenderer.on("startShot", (event: any, msg: any) => {
  ipcRenderer.send('startCapture')
});
ipcRenderer.on("getScreenIdRes", async (event: any, msg: any) => {
  getScreenStream(msg)
});

</script>

<style scoped>
.footer {
  background-color: #fff;
  border: 1px solid #dcdfe6;
  border-bottom-right-radius: 10px;
  border-top-left-radius: 5px;
  height: 58px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.play-bar {
  display: flex;
  justify-content: center;
  align-items: center;
}

.paly-bar-process {
  width: 60%;
}

.time {
  margin-left: 10px;
  font-size: larger;
  position: absolute;
  right: 70px;
  bottom: 18px;
  color: #b32b2b;
}

.arrow_box {
  animation: glow 800ms ease-out infinite alternate;
}

@keyframes glow {
  0% {
    border-color: rgb(245 13 13);
    box-shadow: 0 0 5px rgb(255 0 0), inset 0 0 5px rgb(0 255 0 / 10%), 0 1px 0 #393;
  }

  100% {
    border-color: rgb(111 102 255);
    box-shadow: 0 0 20px rgb(209 119 110 / 60%), inset 0 0 10px rgb(0 255 0 / 40%), 0 1px 0 #8ae512d9;
  }

}
</style>
